package rexsee.xml;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;

import rexsee.core.browser.Browser;
import rexsee.core.browser.clazz.JavascriptInterface;
import rexsee.core.lang.RexseeLanguage;
import android.content.Context;

public class RexseeXML implements JavascriptInterface {

	public static final String INTERFACE_NAME = "XML";
	@Override
	public String getInterfaceName() {
		return mBrowser.application.resources.prefix + INTERFACE_NAME;
	}
	@Override
	public JavascriptInterface getInheritInterface(Browser childBrowser) {
		return this;
	}
	@Override
	public JavascriptInterface getNewInterface(Browser childBrowser) {
		return new RexseeXML(childBrowser);
	}

	public final Context mContext;
	private final Browser mBrowser;

	public static final String NAMESPACE_SPLIT = "__";

	private boolean syncTag = false;
	private String mError = "";

	private String version = null;
	private String encoding = null;
	private Boolean standAlone = null;
	private int depth = 0;
	private final HashMap<String, String> nameSpaces = new HashMap<String, String>();

	private XmlElement mRoot = null;
	private XmlElement mCurrent = null;
	private final ArrayList<XmlElement> mCurrentList = new ArrayList<XmlElement>();

	public RexseeXML(Browser browser) {
		mBrowser = browser;
		mContext = browser.getContext();
	}

	public boolean parse(final String code, boolean showProgress) {

		if (showProgress) mBrowser.progressDialog.show(RexseeLanguage.PROGRESS_ONGOING);
		Thread parseThread = new Thread() {
			@Override
			public void run() {

				XmlPullParserFactory factory;
				try {
					factory = XmlPullParserFactory.newInstance();
					factory.setNamespaceAware(true);
					factory.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
				} catch (XmlPullParserException e) {
					mRoot = null;
					mError = e.getLocalizedMessage();
					syncTag = true;
					return;
				}
				XmlPullParser xpp;
				try {
					xpp = factory.newPullParser();
					xpp.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
				} catch (XmlPullParserException e) {
					mRoot = null;
					mError = e.getLocalizedMessage();
					syncTag = true;
					return;
				}
				try {
					xpp.setInput(new StringReader(code));
				} catch (XmlPullParserException e) {
					mRoot = null;
					mError = e.getLocalizedMessage();
					syncTag = true;
					return;
				}
				int eventType;
				try {
					eventType = xpp.getEventType();
				} catch (XmlPullParserException e) {
					mRoot = null;
					mError = e.getLocalizedMessage();
					syncTag = true;
					return;
				}

				mRoot = new XmlElement(null, "XML", 0, "root");
				XmlElement current = mRoot;
				while (eventType != XmlPullParser.END_DOCUMENT) {
					if (eventType == XmlPullParser.START_DOCUMENT) {
						//Do nothing
					} else if (eventType == XmlPullParser.END_DOCUMENT) {
						//Do nothing
					} else if (eventType == XmlPullParser.START_TAG) {
						String prefix = xpp.getPrefix();
						if (prefix != null && prefix.length() > 0 && !nameSpaces.containsKey(prefix)) {
							nameSpaces.put(prefix, xpp.getNamespace());
						}
						String tagName = (prefix != null && prefix.length() > 0) ? prefix + NAMESPACE_SPLIT + xpp.getName().toLowerCase() : xpp.getName().toLowerCase();
						XmlElement e = new XmlElement(current, tagName, current.depth + 1, "tag");
						depth = Math.max(depth, current.depth + 1);
						int count = xpp.getAttributeCount();
						for (int i = 0; i < count; i++) {
							String attributePrefix = xpp.getAttributePrefix(i);
							if (attributePrefix != null && attributePrefix.length() > 0 && !nameSpaces.containsKey(attributePrefix)) {
								nameSpaces.put(attributePrefix, xpp.getNamespace(attributePrefix));
							}
							String name = (attributePrefix == null) ? xpp.getAttributeName(i) : attributePrefix + NAMESPACE_SPLIT + xpp.getAttributeName(i);
							String value = xpp.getAttributeValue(i);
							e.addAttribute(name.toLowerCase(), value);
						}
						current.addChild(e);
						current = e;
					} else if (eventType == XmlPullParser.END_TAG) {
						current = current.parent;
					} else if (eventType == XmlPullParser.TEXT) {
						String t = xpp.getText().replaceAll("\\s", "");
						if (t.length() > 0) {
							XmlElement e = new XmlElement(current, "", current.depth + 1, "text");
							depth = Math.max(depth, current.depth + 1);
							e.text = t;
							current.addChild(e);
						}
					} else if (eventType == XmlPullParser.CDSECT) {
						XmlElement e = new XmlElement(current, "", current.depth + 1, "cdsect");
						depth = Math.max(depth, current.depth + 1);
						e.text = xpp.getText();
						current.addChild(e);
					} else if (eventType == XmlPullParser.COMMENT) {
						XmlElement e = new XmlElement(current, "", current.depth + 1, "comment");
						depth = Math.max(depth, current.depth + 1);
						e.text = xpp.getText();
						current.addChild(e);
					} else if (eventType == XmlPullParser.DOCDECL) {
						XmlElement e = new XmlElement(current, "", current.depth + 1, "docdecl");
						depth = Math.max(depth, current.depth + 1);
						e.text = xpp.getText();
						current.addChild(e);
					}
					try {
						eventType = xpp.nextToken();
					} catch (Exception e) {
						mRoot = null;
						mError = e.getLocalizedMessage();
						syncTag = true;
						return;
					}
				}

				version = String.valueOf(xpp.getProperty("http://xmlpull.org/v1/doc/properties.html#xmldecl-version"));
				encoding = String.valueOf(xpp.getInputEncoding());
				standAlone = (Boolean) xpp.getProperty("http://xmlpull.org/v1/doc/features.html#xmldecl-standalone");
				syncTag = true;
				return;

			}
		};
		syncTag = false;
		mError = "";
		version = null;
		encoding = null;
		standAlone = null;
		nameSpaces.clear();
		mRoot = null;
		mCurrent = null;
		mCurrentList.clear();
		parseThread.start();
		while (!syncTag) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
			}
		}
		parseThread = null;
		if (showProgress) mBrowser.progressDialog.hide();
		if (mRoot == null) {
			mBrowser.exception(getInterfaceName(), mError);
			return false;
		} else {
			mCurrent = mRoot;
			return true;
		}

	}

	public String getVersion() {
		return version;
	}
	public String getEncoding() {
		return encoding;
	}
	public String getStandAlone() {
		return (standAlone == null) ? null : ((standAlone) ? "yes" : "no");
	}
	public String getNameSpaces() {
		String rtn = "";
		Set<String> nameSet = nameSpaces.keySet();
		Iterator<String> nameIterator = nameSet.iterator();
		while (nameIterator.hasNext()) {
			if (!rtn.equals("")) rtn += ",";
			String name = nameIterator.next();
			String value = nameSpaces.get(name);
			rtn += "\"" + name + "\":\"" + value + "\"";
		}
		return "{" + rtn + "}";
	}
	public int getMaxDepth() {
		return depth;
	}

	public String getJson() {
		return (mCurrent == null) ? "" : mCurrent.toString();
	}
	public String getTagName() {
		return (mCurrent == null) ? "" : mCurrent.tagName;
	}
	public String getType() {
		return (mCurrent == null) ? "" : mCurrent.type;
	}
	public int getDepth() {
		return (mCurrent == null) ? -1 : mCurrent.depth;
	}
	public String getText() {
		return (mCurrent == null) ? null : mCurrent.text;
	}
	public String getAttribute(String attributeName) {
		return (mCurrent == null || mCurrent.attributes == null || !mCurrent.attributes.containsKey(attributeName)) ? "" : mCurrent.attributes.get(attributeName);
	}
	public int getChildLength() {
		return (mCurrent == null || mCurrent.childs == null) ? 0 : mCurrent.childs.size();
	}
	public boolean hasParent() {
		return (mCurrent == null || mCurrent.parent == null) ? false : true;
	}
	public int getIndexOf() {
		return (mCurrent == null || mCurrent.parent == null) ? -1 : mCurrent.parent.childs.indexOf(mCurrent);
	}

	public boolean goToRoot() {
		mCurrent = mRoot;
		return (mCurrent != null) ? true : false;
	}
	public boolean goToParent() {
		if (mCurrent == null || mCurrent.parent == null) {
			return false;
		} else {
			mCurrent = mCurrent.parent;
			return true;
		}
	}
	public boolean goToChild(int index) {
		if (mCurrent == null || mCurrent.childs == null || mCurrent.childs.size() <= index) {
			return false;
		} else {
			mCurrent = mCurrent.childs.get(index);
			return true;
		}
	}
	public boolean goToNextSibling() {
		int index = getIndexOf();
		if (index < 0 || mCurrent.parent.childs.size() <= index + 1) {
			return false;
		} else {
			mCurrent = mCurrent.parent.childs.get(index + 1);
			return true;
		}
	}
	public boolean goPreviousSibling() {
		int index = getIndexOf();
		if (index <= 0) {
			return false;
		} else {
			mCurrent = mCurrent.parent.childs.get(index - 1);
			return true;
		}
	}

	public boolean goToList(int index) {
		if (mCurrentList == null || mCurrentList.size() <= index) {
			return false;
		} else {
			mCurrent = mCurrentList.get(index);
			return true;
		}
	}

	public int getListLength() {
		return (mCurrentList == null) ? 0 : mCurrentList.size();
	}
	public int getListByAttribute(String attributeName, String attributeValue) {
		mCurrentList.clear();
		if (mRoot == null) return 0;
		mRoot.getChildsByAttribute(mCurrentList, attributeName, attributeValue);
		return mCurrentList.size();
	}
	public int getListByTagName(String tagName) {
		mCurrentList.clear();
		if (mRoot == null) return 0;
		mRoot.getChildsByTagName(mCurrentList, tagName);
		return mCurrentList.size();
	}
	public int getListByType(String type) {
		mCurrentList.clear();
		if (mRoot == null) return 0;
		mRoot.getChildsByType(mCurrentList, type);
		return mCurrentList.size();
	}
	public int getListByDepth(int depth) {
		mCurrentList.clear();
		if (mRoot == null) return 0;
		mRoot.getChildsByDepth(mCurrentList, depth);
		return mCurrentList.size();
	}

}
